package com.bestcem.xm.survey.service.impl;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.bestcem.xm.common.core.exception.BusinessException;
import com.bestcem.xm.qdes.grpc.v1.services.Project;
import com.bestcem.xm.qdes.grpc.v1.services.QStruct;
import com.bestcem.xm.survey.entity.SurveyRespondent;
import com.bestcem.xm.survey.entity.SurveyRespondentData;
import com.bestcem.xm.survey.entity.SurveyRespondentHashQuestionList;
import com.bestcem.xm.survey.grpc.client.QdesGrpcClient;
import com.bestcem.xm.survey.mapper.SurveyRespondentDataMapper;
import com.bestcem.xm.survey.mapper.SurveyRespondentHashQuestionListMapper;
import com.bestcem.xm.survey.mapper.SurveyRespondentMapper;
import com.bestcem.xm.survey.service.SurveyCommonService;
import com.bestcem.xm.survey.service.SurveyReportService;
import com.bestcem.xm.survey.service.reportHandler.BaseReportHandler;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

import static com.bestcem.xm.survey.enums.RespondentSourceEnum.RESPONDENT_SOURCE_PREVIEW;

/**
 * @author guita
 * @description 针对表【ds_survey_quotacondition】的数据库操作Service实现
 * @createDate 2023-01-09 15:15:37
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class SurveyReportServiceImpl implements SurveyReportService {
    private final Map<String, BaseReportHandler> handlerMap = new HashMap<>(8);
    private @NonNull SurveyRespondentMapper surveyRespondentMapper;
    private @NonNull SurveyRespondentDataMapper surveyRespondentDataMapper;
    private @NonNull SurveyCommonService surveyCommonService;
    private @NonNull SurveyRespondentHashQuestionListMapper surveyRespondentHashQuestionListMapper;
    private @NonNull QdesGrpcClient qdesGrpcClient;
    private @NonNull List<BaseReportHandler> baseTypeHandler;

    /**
     * 初始化
     */
    @PostConstruct
    public void loadAllQuestionHandler() {
        for (BaseReportHandler handler : baseTypeHandler) {
            for (String type : handler.getQuestionTypeValue()) {
                handlerMap.put(type, handler);
            }
        }
    }

    @Override
    public JSONObject getReport(String projectId, Integer source, JSONObject param) {
        log.info("Now to get report");
        Project project = qdesGrpcClient.getProject(projectId);
        if (project == null) {
            throw new BusinessException("project not found");
        }
        JSONObject allParams = new JSONObject();
        String paramSig = "";
        if (RESPONDENT_SOURCE_PREVIEW.getValue().equals(source)) {
            allParams.put("source", source);
            paramSig = "";
        } else {
            allParams = Optional.ofNullable(param.getJSONObject("allParams")).orElse(new JSONObject());
            paramSig = Optional.ofNullable(allParams.getString("sig")).orElse("");
        }

        Long seq = param.getLong("seq");
        if (seq == null) {
            throw new BusinessException("seq not found");
        }

        SurveyRespondent rspd = getRespondentBySeq(projectId, seq);
        if (rspd == null || !projectId.equals(rspd.getProjectId())) {
            throw new BusinessException(StrUtil.format("Respondent[seq={}] not found", seq));
        }

        SurveyRespondentData rspdData = getRespondentdataBySeq(seq, projectId);
        if (rspdData == null) {
            throw new BusinessException(StrUtil.format("Respondent_data[seq={}] not found", seq));
        }

        // 获取qstruct
        Integer version = rspd.getVersion();
        List<QStruct> qStructs = qdesGrpcClient.qdesListQstructWithq(projectId, version);
        if (CollectionUtils.isEmpty(qStructs)) {
            throw new BusinessException(StrUtil.format("qstruct[seq={}] not found", seq));
        }

        List<QStruct> newestQstruct = new ArrayList<>();
        if (RESPONDENT_SOURCE_PREVIEW.getValue().equals(source)) {
            // 预览情况下，没有发布问卷
            // newest_qstruct = await qdes_list_qstruct(projectID=project_id, version=project.version)
            newestQstruct = qStructs;
        } else {
            newestQstruct = qdesGrpcClient.qdesListQstructWithq(projectId, project.getReleaseVer());
        }
        if (CollectionUtils.isEmpty(newestQstruct)) {
            throw new BusinessException(StrUtil.format("newestQstruct[seq={}] not found", seq));
        }
        // 评分规则
        JSONObject scores = JSONObject.parseObject(newestQstruct.get(0).getScores());
        JSONObject scoreConfig = JSONObject.parseObject(newestQstruct.get(0).getScoreConfig());
        JSONObject answers = JSONObject.parseObject(rspdData.getAnswers());
        List<JSONObject> questions = surveyCommonService.walkQstructQuestions(JSONObject.parseObject(qStructs.get(0).getData()));
        Map<String, JSONObject> qQuestions = questions.stream().collect(Collectors.toMap(
            a -> a.getString("gid"),
            a -> a
        ));
        // 答题题目及选项顺序 从关联表RespondentHashQuestionList中取
        SurveyRespondentHashQuestionList respondentHashQuestionList = getOneRespondentHashQuestionList(rspdData.getHashQListKey());
        JSONArray questionList = JSONArray.parseArray(respondentHashQuestionList.getQuestionList());
        questionList = questionList == null ? new JSONArray() : questionList;
        double maximumScore = 0d;  // 总分
        double totalScore = 0d;  // 总得分

        for (int i = 0; i < questionList.size(); i++) {
            JSONObject question = questionList.getJSONObject(i);
            if (question == null || !question.containsKey("gid")) {
                continue;
            }
            String gid = question.getString("gid");
            JSONObject q = qQuestions.get(gid);
            if (q == null) {
                continue;
            }
            BaseReportHandler qScoreHelp = handlerMap.get(q.getString("qtype"));
            if (qScoreHelp != null) {
                question.put("q_total_score", qScoreHelp.getMaxScore(q, Optional.ofNullable(scores.getJSONArray(gid)).orElse(new JSONArray())));
                question.put("q_score", qScoreHelp.getScore(q, answers.get(gid), Optional.ofNullable(scores.getJSONArray(gid)).orElse(new JSONArray()), scoreConfig));
                maximumScore += question.getDouble("q_total_score");
                totalScore += question.getDouble("q_score");
            }
        }
        // 总分保留2位小数
        maximumScore = new BigDecimal(maximumScore).setScale(2, RoundingMode.HALF_UP).stripTrailingZeros().doubleValue();
        totalScore = new BigDecimal(totalScore).setScale(2, RoundingMode.HALF_UP).stripTrailingZeros().doubleValue();
        JSONObject result = new JSONObject();
        result.put("questions", questions);
        result.put("scores", scores);
        result.put("answers", answers);
        result.put("version", version);
        result.put("project_id", projectId);
        result.put("report_url", rspdData.getReportUrl());
        result.put("question_list", questionList);
        result.put("maximum_score", maximumScore);
        result.put("total_score", totalScore);
        result.put("survey_title", project.getTitle());
        return result;
    }

    private SurveyRespondent getRespondentBySeq(String projectId, Long seq) {
        return surveyRespondentMapper.selectOne(new LambdaQueryWrapper<SurveyRespondent>()
            .eq(SurveyRespondent::getProjectId, projectId)
            .eq(SurveyRespondent::getSeq, seq)
            .last("limit 1"));
    }

    private SurveyRespondentData getRespondentdataBySeq(Long seq, String projectId) {
        return surveyRespondentDataMapper.selectOne(new LambdaQueryWrapper<SurveyRespondentData>()
            .eq(SurveyRespondentData::getSeq, seq)
            .eq(SurveyRespondentData::getProjectId, projectId)
            .last("limit 1"));
    }

    private SurveyRespondentHashQuestionList getOneRespondentHashQuestionList(String hashkey) {
        return surveyRespondentHashQuestionListMapper.selectOne(new LambdaQueryWrapper<SurveyRespondentHashQuestionList>()
            .eq(SurveyRespondentHashQuestionList::getHashkey, hashkey)
            .last("limit 1"));
    }
}